/*
* License:  This  program  is  free  software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the  Free Software Foundation; either version 3 of the License, or (at your
* option)  any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*/

package pcgen.CharacterViewer.resources;

import org.json.JSONException;
import org.json.JSONObject;

import android.os.Parcel;
import android.os.Parcelable;

import pcgen.android.Logger;

import pcgen.CharacterViewer.resources.GameResourceModifier.ModifierTypes;

public class GameResourceAttributeDamage extends GameResourceAttribute
{
	public GameResourceAttributeDamage()
	{	
		setType(Types.Damage);
		setAbilityType(GameAbilities.Constitution);
	}
	
	public GameResourceAttributeDamage(boolean isPrimary)
	{	
		_isPrimary = isPrimary;
		setType(Types.Damage);
		setAbilityType(GameAbilities.Constitution);
	}
	
	public GameResourceAttributeDamage(Parcel in)
	{
		super(in);
		setType(Types.Damage);
		setAbilityType(GameAbilities.Constitution);
	}
	
	public static final Parcelable.Creator<GameResourceAttributeDamage> CREATOR = new Parcelable.Creator<GameResourceAttributeDamage>() 
	{
		public GameResourceAttributeDamage createFromParcel(Parcel in) 
		{
		    return new GameResourceAttributeDamage(in);
		}
		
		public GameResourceAttributeDamage[] newArray(int size) 
		{
		    return new GameResourceAttributeDamage[size];
		}
	};
	
	public int getAbilityDamageModifier()
	{
		return getAbility().getAbilityModifier() * getCharacter().getLevel();
	}
	
	@Override
	public void addModifier(GameResourceModifier modifier)
	{
		super.addModifier(modifier);
		
		if (getFolder().getSettingsDamageTypeHitPoints())
		{
			if (modifier.getModifierType() == ModifierTypes.Temp)
				_secondary.addModifier(modifier);
		}
		
		onValueModified();
	}
	
	@Override
	public int getValue()
	{
		int value = 0;
		if (_isPrimary)
		{
			if (getFolder().getSettingsDamageTypeHitPoints())
				// Hit Points
				value = getValueBase() + getAbilityDamageModifier() + getModifier() + getMisc();
			else if (getFolder().getSettingsDamageTypeVitalityPoints())
				// Vitality Points
				value = getValueBase() + getAbilityDamageModifier() + getModifier() + getMisc();
		}
		else
		{
			if (getFolder().getSettingsDamageTypeHitPoints())
				// Subdual
				value = getModifier() + getMisc();
			else if (getFolder().getSettingsDamageTypeVitalityPoints())
				// Wounds
				value = getAbility().getValue() + getModifier() + getMisc();
		}
		
		if (value < getValueMin())
			return getValueMin();
		
		if (value > getValueMax())
			return getValueMax();
		
		return value;
	}
	
	public int getValueMax()
	{
		int value = 0;
		if (_isPrimary)
		{
			if (getFolder().getSettingsDamageTypeHitPoints())
				// Hit Points
				value = getValueBase() + getAbilityDamageModifier() + getValueTemp();
			else if (getFolder().getSettingsDamageTypeVitalityPoints())
				// Vitality Points
				value = getValueBase() + getAbilityDamageModifier() + getValueTemp();
		}
		else
		{
			if (getFolder().getSettingsDamageTypeHitPoints())
				// Subdual
				value = getValueBase() + getAbilityDamageModifier() + getValueTemp();
			else if (getFolder().getSettingsDamageTypeVitalityPoints())
				// Wounds
				value = getAbility().getValue() + getValueTemp();
		}
		return value;
	}
	
	public int getValueMaxDamage()
	{
		return getValueMax();
	}
	
	public int getValueMaxDamageAdjustment()
	{
		int value = getValue() + Math.abs(getValueMin());
		if (_isPrimary && (getFolder().getSettingsDamageTypeVitalityPoints()))
			return value + (_secondary != null ? _secondary.getValueMaxDamageAdjustment() : 0);
		
		return value;
	}
	
	public int getValueMaxHeal()
	{
		int value = Math.abs(getModifierDamageAndHealing());
		int valueMax = getValueMax();
		
		// If for whatever reason the damage taken is greater
		// than the base value plus non-damage modifiers and miscellaneous
		// then the latter is now the maximum that can be healed.
		if (value > valueMax)
			value = valueMax;
		
		return value;
	}
	
	public int getValueMaxHealAdjustment()
	{
		return getValueMaxHeal();
	}
	
	public int getValueMin()
	{
		if (_valueMin != Integer.MIN_VALUE)
			return _valueMin;

		int value = 0;
		if (_isPrimary)
		{
			if (getFolder().getSettingsDamageTypeHitPoints())
				// Hit Points
				value = -10; // TODO: Need a setting to determine if its -10 or the constitution modifier negative.
			else if (getFolder().getSettingsDamageTypeVitalityPoints())
				// Vitality Points
				value = 0;
		}
		else
		{
			if (getFolder().getSettingsDamageTypeHitPoints())
				// Subdual
				value = 0;
			else if (getFolder().getSettingsDamageTypeVitalityPoints())
				// Wounds
				value = -10;
		}
		return value;
	}
	
	public int getValueMinDamage()
	{
		int value = getValueMin();
		
		if (_isPrimary && (getFolder().getSettingsDamageTypeVitalityPoints()))
			value += (_secondary != null ? _secondary.getValueMaxDamage() : 0);
		
		return value;
	}
	
//	public String getValueStats()
//	{
//		int value = getValue();
//		if (getFolder().getSettingsDamageTypeHitPoints())
//		{
//			if (_isPrimary)
//			{
//				if (value == 0)
//					return "Unconcious";
//				if (value < getValueMin())
//					return "Dead";
//				if (value < 0)
//					return "Dying";
//			}
//		}
//	}
	
	public int getValueTemp()
	{
		return getModifierTemp() + getMisc();
	}
	
	@Override
	public JSONObject load(JSONObject data)
		throws JSONException
	{
		super.load(data);
		
		if (data == null)
			return null;
		
		setValueCurrent(getJSONInt(data, "valueCurrent"));
		setValueMin(getJSONInt(data, "valueMin", Integer.MIN_VALUE));
		_isPrimary = getJSONBoolean(data, "isPrimary");
	    
	    return data;
	}
	
	@Override
	public void onValueModified()
	{
		if (onValueModifiedIgnore)
			return;
		
		// If the value of the healing is greater than or equal to
		// the damage, then clean it out.
		removeDamageAndHealing();
		
		super.onValueModified();
	}
	
	public void modifyValueCurrentDamage(int value)
	{
		try
		{
			onValueModifiedIgnore = true;
			value = (value > 0 ? value * -1 : value);
			// Subudual damage - this is additive instead of subtractive
			if (!_isPrimary && (getFolder().getSettingsDamageTypeHitPoints()))
				value = (value < 0 ? value * -1 : value);
			else
			{
				// we have temporary health, use it up first...
				int temp = getModifierTemp();
				if (temp > 0)
				{
					for(GameResourceModifier modifier : getModifiers())
					{
						if (modifier.getModifierType() != ModifierTypes.Temp)
							continue;
						
						if (value <= 0)
							break;
						
						int modifierValue = modifier.getValue();
						if (value >= modifierValue)
						{
							value -= modifierValue;
							modifierValue = 0;
						}
						modifier.setValue(modifierValue);
					}
				}
			}
			
			int additional = 0;
			
			if (Math.abs(value) > 0)
			{
				int damage = value;
				if (_isPrimary && (getFolder().getSettingsDamageTypeVitalityPoints()))
				{
					// If the absolute value of the damage is greater than the current value
					// then the rest applies to wound points...
					if (Math.abs(damage) > getValue())
					{
						additional = getValue() + damage;
						damage = getValue() * -1;
					}
				}
				
				addModifier(GameResourceModifier.ModifierTypes.Damage, damage);
			}
			
			if (getFolder().getSettingsDamageTypeVitalityPoints())
			{
				if (_isPrimary)
				{
					if ((Math.abs(additional) > 0) && (_secondary != null))
						_secondary.modifyValueCurrentDamage(additional);
				}
			}
			
			// Either hit points or wounds..
			if ((getFolder().getSettingsDamageTypeVitalityPoints()) ||
				(getFolder().getSettingsDamageTypeVitalityPoints()) && !_isPrimary)
				getCharacter().setCondition(GameTypesCondition.TemporaryDamaged);

			onValueModifiedIgnore = false;
			onValueModified();
		}
		finally
		{
			onValueModifiedIgnore = false;
		}
	}
	
	public void modifyValueCurrentHealing(int value, boolean special)
	{
		try
		{
			onValueModifiedIgnore = true;
			
			value = (value > 0 ? value : value * -1);
			// Subudual damage - this is subtractive instead of additive
			if (!_isPrimary && getFolder().getSettingsDamageTypeHitPoints())
				value = (value < 0 ? value : value * -1);
				
			addModifier(GameResourceModifier.ModifierTypes.Healing, value);
			
			// Hey, if healing primary damage, then also heal secondary damage... although this really needs to check
			// for magical healing only
			if (special && getFolder().getSettingsDamageHealingSpecial())
			{
				if (_isPrimary && getFolder().getSettingsDamageTypeHitPoints())
				{
					if (_secondary != null)
						_secondary.addModifier(GameResourceModifier.ModifierTypes.Healing, value);
				}
			}
			
			if (value > 0)
			{
				if (_isPrimary && (getFolder().getSettingsDamageTypeVitalityPoints()))
					getCharacter().removeCondition(GameTypesCondition.Fatigued);
			}
			
			getCharacter().setCondition(GameTypesCondition.TemporaryHealed);
			
			onValueModifiedIgnore = false;
			onValueModified();
		}
		finally
		{
			onValueModifiedIgnore = false;
		}
	}
	
	@Override
	public void removeModifier(GameResourceModifier modifier)
	{
		super.removeModifier(modifier);
		
		if (getFolder().getSettingsDamageTypeHitPoints())
		{
			if (modifier.getModifierType() == ModifierTypes.Temp)
				_secondary.removeModifier(modifier);
		}
		
		onValueModified();
	}
	
	@Override
	public JSONObject save() 
		throws JSONException
	{
	    JSONObject data = super.save();

	    data.put("valueCurrent", _valueCurrent);
	    data.put("valueMin", _valueMin);
	    data.put("isPrimary", _isPrimary);
	    
	    return data;
	}
	
	public void setSecondary(GameResourceAttributeDamage secondary)
	{
		_secondary = secondary;
	}
	
	public void setValueCurrent(int value)
	{
		_valueCurrent = value;
	}
	
	public void setValueMin(int value)
	{
		_valueMin = value;
	}

	@Override
	protected void readFromParcelTransformEx(Parcel in) 
		throws Throwable
	{
		try
		{
			super.readFromParcelTransformEx(in);
			
			setValueCurrent(in.readInt());
			setValueMin(in.readInt());
			_isPrimary = (in.readInt() == 1 ? true : false);
		}
    	catch (Throwable tr)
    	{
    		Logger.e(TAG, "readFromParcelTransformEx", tr);
    		throw tr;
    	}
	}

	@Override
	protected void writeToParcelTransformEx(Parcel dest, int flags)
		throws Throwable
	{
		try
		{
			super.writeToParcelTransformEx(dest, flags);
			
			dest.writeInt(_valueCurrent);
			dest.writeInt(_valueMin);
			dest.writeInt(_isPrimary ? 1: 0);
		}
    	catch (Throwable tr)
    	{
    		Logger.e(TAG, "writeToParcelTransformEx", tr);
    		throw tr;
    	}
	}

	private boolean _isPrimary;
	private int _valueCurrent;
	private int _valueMin = Integer.MIN_VALUE;
	private GameResourceAttributeDamage _secondary;

	private static final String TAG = GameResourceAttributeDamage.class.getSimpleName();
}
